package com.clj.fastble;

import com.clj.fastble.bluetooth.BleBluetooth;
import com.clj.fastble.bluetooth.MultipleBluetoothController;
import com.clj.fastble.bluetooth.SplitWriter;
import com.clj.fastble.callback.BleGattCallback;
import com.clj.fastble.callback.BleIndicateCallback;
import com.clj.fastble.callback.BleMtuChangedCallback;
import com.clj.fastble.callback.BleNotifyCallback;
import com.clj.fastble.callback.BleReadCallback;
import com.clj.fastble.callback.BleRssiCallback;
import com.clj.fastble.callback.BleScanCallback;
import com.clj.fastble.callback.BleWriteCallback;
import com.clj.fastble.callback.BlueToothScanCallback;
import com.clj.fastble.data.BleDevice;
import com.clj.fastble.data.BleMsg;
import com.clj.fastble.data.BleScanState;
import com.clj.fastble.exception.OtherException;
import com.clj.fastble.scan.BleScanRuleConfig;
import com.clj.fastble.scan.BleScanner;
import com.clj.fastble.utils.BleLog;

import ohos.app.Context;
import ohos.bluetooth.BluetoothHost;
import ohos.bluetooth.ProfileBase;
import ohos.bluetooth.ble.BlePeripheralDevice;
import ohos.bluetooth.ble.BleScanFilter;
import ohos.bluetooth.ble.BleScanResult;
import ohos.bluetooth.ble.GattCharacteristic;
import ohos.bluetooth.ble.GattService;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.eventhandler.InnerEvent;

import java.util.List;
import java.util.Map;

/**
 * BleManager
 */
public class BleManager {
    private Context context;
    private BleScanRuleConfig bleScanRuleConfig;
    private BluetoothHost bluetoothAdapter;
    private MultipleBluetoothController multipleBluetoothController;
    private BluetoothHost bluetoothManager;

    /**
     * DEFAULT_SCAN_TIME
     */
    public static final int DEFAULT_SCAN_TIME = 10000;
    private static final int DEFAULT_MAX_MULTIPLE_DEVICE = 7;
    private static final int DEFAULT_OPERATE_TIME = 5000;
    private static final int DEFAULT_CONNECT_RETRY_COUNT = 0;
    private static final int DEFAULT_CONNECT_RETRY_INTERVAL = 5000;
    private static final int DEFAULT_MTU = 23;
    private static final int DEFAULT_MAX_MTU = 512;
    private static final int DEFAULT_WRITE_DATA_SPLIT_COUNT = 20;
    private static final int DEFAULT_CONNECT_OVER_TIME = 10000;

    private int maxConnectCount = DEFAULT_MAX_MULTIPLE_DEVICE;
    private int operateTimeout = DEFAULT_OPERATE_TIME;
    private int reConnectCount = DEFAULT_CONNECT_RETRY_COUNT;
    private long reConnectInterval = DEFAULT_CONNECT_RETRY_INTERVAL;
    private int splitWriteNum = DEFAULT_WRITE_DATA_SPLIT_COUNT;
    private long connectOverTime = DEFAULT_CONNECT_OVER_TIME;

    /**
     * getInstance
     *
     * @return BleManager
     */
    public static BleManager getInstance() {
        return BleManagerHolder.SBLE_MANAGER;
    }

    private static class BleManagerHolder {
        private static final BleManager SBLE_MANAGER = new BleManager();
    }

    /**
     * init
     *
     * @param app app
     */
    public void init(Context app) {
        context = app;
        bluetoothManager = BluetoothHost.getDefaultHost(app);
        bluetoothAdapter = bluetoothManager;

        multipleBluetoothController = new MultipleBluetoothController();
        bleScanRuleConfig = new BleScanRuleConfig();
    }

    /**
     * Get the Context
     *
     * @return context
     */
    public Context getContext() {
        return context;
    }

    /**
     * Get the BluetoothManager
     *
     * @return BluetoothHost
     */
    public BluetoothHost getBluetoothManager() {
        return bluetoothManager;
    }

    /**
     * Get the BluetoothAdapter
     *
     * @return BluetoothHost
     */
    public BluetoothHost getBluetoothAdapter() {
        return bluetoothAdapter;
    }

    /**
     * get the ScanRuleConfig
     *
     * @return BleScanRuleConfig
     */
    public BleScanRuleConfig getScanRuleConfig() {
        return bleScanRuleConfig;
    }

    /**
     * Get the multiple Bluetooth Controller
     *
     * @return MultipleBluetoothController
     */
    public MultipleBluetoothController getMultipleBluetoothController() {
        return multipleBluetoothController;
    }

    /**
     * Configure scan and connection properties
     *
     * @param config config
     */
    public void initScanRule(BleScanRuleConfig config) {
        this.bleScanRuleConfig = config;
    }

    /**
     * Get the maximum number of connections
     *
     * @return int
     */
    public int getMaxConnectCount() {
        return maxConnectCount;
    }

    /**
     * Set the maximum number of connections
     *
     * @param count count
     * @return BleManager
     */
    public BleManager setMaxConnectCount(int count) {
        if (count > DEFAULT_MAX_MULTIPLE_DEVICE) {
            count = DEFAULT_MAX_MULTIPLE_DEVICE;
        }
        this.maxConnectCount = count;
        return this;
    }

    /**
     * Get operate timeout
     *
     * @return int
     */
    public int getOperateTimeout() {
        return operateTimeout;
    }

    /**
     * Set operate timeout
     *
     * @param count count
     * @return BleManager
     */
    public BleManager setOperateTimeout(int count) {
        this.operateTimeout = count;
        return this;
    }

    /**
     * Get connect retry count
     *
     * @return int
     */
    public int getReConnectCount() {
        return reConnectCount;
    }

    /**
     * Get connect retry interval
     *
     * @return long
     */
    public long getReConnectInterval() {
        return reConnectInterval;
    }

    /**
     * Set connect retry count and interval
     *
     * @param count count
     * @return BleManager
     */
    public BleManager setReConnectCount(int count) {
        return setReConnectCount(count, DEFAULT_CONNECT_RETRY_INTERVAL);
    }

    /**
     * Set connect retry count and interval
     *
     * @param count    count
     * @param interval int
     * @return BleManager
     */
    public BleManager setReConnectCount(int count, long interval) {
        if (count > 10) {
            count = 10;
        }
        if (interval < 0) {
            interval = 0;
        }
        this.reConnectCount = count;
        this.reConnectInterval = interval;
        return this;
    }

    /**
     * Get operate split Write Num
     *
     * @return int
     */
    public int getSplitWriteNum() {
        return splitWriteNum;
    }

    /**
     * Set split Writ eNum
     *
     * @param num num
     * @return BleManager
     */
    public BleManager setSplitWriteNum(int num) {
        if (num > 0) {
            this.splitWriteNum = num;
        }
        return this;
    }

    /**
     * Get operate connect Over Time
     *
     * @return long
     */
    public long getConnectOverTime() {
        return connectOverTime;
    }

    /**
     * Set connect Over Time
     *
     * @param time time
     * @return BleManager
     */
    public BleManager setConnectOverTime(long time) {
        if (time <= 0) {
            time = 100;
        }
        this.connectOverTime = time;
        return this;
    }

    /**
     * print log?
     *
     * @param enable enable
     * @return BleManager
     */
    public BleManager enableLog(boolean enable) {
        BleLog.isPrint = enable;
        return this;
    }

    /**
     * bluetooth scan device around
     * 用于普通蓝牙扫描 ，ble在openHarmony中，无奈
     *
     * @param callback callback
     */
    public void bluetToothScan(BlueToothScanCallback callback) {
        if (callback == null) {
            throw new IllegalArgumentException("BleScanCallback can not be Null!");
        }

        if (!isBlueEnable()) {
            BleLog.error("Bluetooth not enable!");
            callback.notifyDiscoveryState(false);
            return;
        }

        if (!bluetoothManager.isBtDiscovering()) {
            bluetoothManager.startBtDiscovery();
        }

        BleScanner.getInstance().blueToothScan(context, callback);
    }

    /**
     * 取消普通蓝牙扫描
     */
    public void cancelBlueToothScan() {
        if (bluetoothManager != null) {
            bluetoothManager.cancelBtDiscovery();
            unSubscribeBluetooth();
            BleLog.info("scanEvent", "stop scan");
        }
    }

    /**
     * ble scan device around
     *
     * @param callback callback
     */
    public void scan(BleScanCallback callback) {
        if (callback == null) {
            throw new IllegalArgumentException("BleScanCallback can not be Null!");
        }

        if (!isBlueEnable()) {
            BleLog.error("Bluetooth not enable!");
            callback.onScanStarted(false);
            return;
        }

        List<BleScanFilter> filters = bleScanRuleConfig.getFilters();
        long timeOut = bleScanRuleConfig.getScanTimeOut();

        BleScanner.getInstance().scan(context, filters, timeOut, callback);
    }

    /**
     * scan device then connect
     *
     * @param callback callback
     */
    @Deprecated
    public void scanAndConnect(BleScanCallback callback) {
        if (callback == null) {
            throw new IllegalArgumentException("BleScanAndConnectCallback can not be Null!");
        }

        if (!isBlueEnable()) {
            BleLog.error("Bluetooth not enable!");
            callback.onScanStarted(false);
            return;
        }

        List<BleScanFilter> filters = bleScanRuleConfig.getFilters();
        long timeOut = bleScanRuleConfig.getScanTimeOut();

        BleScanner.getInstance().scanAndConnect(context, filters, timeOut, callback);
    }

    /**
     * connect a known device
     *
     * @param bleDevice       bleDevice
     * @param bleGattCallback bleGattCallback
     * @return BlePeripheralDevice
     */
    public BlePeripheralDevice connect(BleDevice bleDevice, BleGattCallback bleGattCallback) {
        if (bleGattCallback == null) {
            throw new IllegalArgumentException("BleGattCallback can not be Null!");
        }

        if (!isBlueEnable()) {
            BleLog.error("Bluetooth not enable!");
            bleGattCallback.onConnectFail(bleDevice, new OtherException("Bluetooth not enable!"));
            return null;
        }

        if (EventRunner.current() == null || EventRunner.current() != EventRunner.getMainEventRunner()) {
            BleLog.worn("Be careful: currentThread is not MainThread!");
        }

        if (bleDevice == null || bleDevice.getDevice() == null) {
            bleGattCallback.onConnectFail(bleDevice, new OtherException("Not Found Device Exception Occurred!"));
        } else {
            BleBluetooth bleBluetooth = multipleBluetoothController.buildConnectingBle(bleDevice);
            boolean autoConnect = bleScanRuleConfig.isAutoConnect();
            return bleBluetooth.connect(bleDevice, autoConnect, bleGattCallback);
        }

        return null;
    }


    /**
     * Cancel ble scan
     */
    public void cancelScan() {
        BleScanner.getInstance().stopLeScan();
    }

    /**
     * notify
     *
     * @param bleDevice    bleDevice
     * @param uuid_service uuid_service
     * @param uuid_notify  uuid_notify
     * @param callback     callback
     */
    @Deprecated
    public void notify(BleDevice bleDevice, String uuid_service, String uuid_notify, BleNotifyCallback callback) {
        notify(bleDevice, uuid_service, uuid_notify, false, callback);
    }

    /**
     * notify
     *
     * @param bleDevice                   bleDevice
     * @param uuid_service                uuid_service
     * @param uuid_notify                 uuid_notify
     * @param useCharacteristicDescriptor useCharacteristicDescriptor
     * @param callback                    callback
     */
    @Deprecated
    public void notify(
            BleDevice bleDevice,
            String uuid_service,
            String uuid_notify,
            boolean useCharacteristicDescriptor,
            BleNotifyCallback callback) {
        if (callback == null) {
            throw new IllegalArgumentException("BleNotifyCallback can not be Null!");
        }

        BleBluetooth bleBluetooth = multipleBluetoothController.getBleBluetooth(bleDevice);
        if (bleBluetooth == null) {
            callback.onNotifyFailure(new OtherException("This device not connect!"));
        } else {
            bleBluetooth
                    .newBleConnector()
                    .withUUIDString(uuid_service, uuid_notify)
                    .enableCharacteristicNotify(callback, uuid_notify, useCharacteristicDescriptor);
        }
    }

    /**
     * indicate
     *
     * @param bleDevice     bleDevice
     * @param uuid_service  uuid_service
     * @param uuid_indicate uuid_indicate
     * @param callback      callback
     */
    @Deprecated
    public void indicate(BleDevice bleDevice, String uuid_service, String uuid_indicate, BleIndicateCallback callback) {
        indicate(bleDevice, uuid_service, uuid_indicate, false, callback);
    }

    /**
     * indicate
     *
     * @param bleDevice                   bleDevice
     * @param uuid_service                uuid_service
     * @param uuid_indicate               uuid_indicate
     * @param useCharacteristicDescriptor useCharacteristicDescriptor
     * @param callback                    callback
     */
    @Deprecated
    public void indicate(
            BleDevice bleDevice,
            String uuid_service,
            String uuid_indicate,
            boolean useCharacteristicDescriptor,
            BleIndicateCallback callback) {
        if (callback == null) {
            throw new IllegalArgumentException("BleIndicateCallback can not be Null!");
        }

        BleBluetooth bleBluetooth = multipleBluetoothController.getBleBluetooth(bleDevice);
        if (bleBluetooth == null) {
            callback.onIndicateFailure(new OtherException("This device not connect!"));
        } else {
            bleBluetooth
                    .newBleConnector()
                    .withUUIDString(uuid_service, uuid_indicate)
                    .enableCharacteristicIndicate(callback, uuid_indicate, useCharacteristicDescriptor);
        }
    }

    /**
     * stop notify, remove callback
     *
     * @param bleDevice    bleDevice
     * @param uuid_service uuid_service
     * @param uuid_notify  uuid_notify
     * @return boolean
     */
    @Deprecated
    public boolean stopNotify(BleDevice bleDevice, String uuid_service, String uuid_notify) {
        return stopNotify(bleDevice, uuid_service, uuid_notify, false);
    }

    /**
     * stop notify, remove callback
     *
     * @param bleDevice                   bleDevice
     * @param uuid_service                uuid_service
     * @param uuid_notify                 uuid_notify
     * @param useCharacteristicDescriptor useCharacteristicDescriptor
     * @return boolean
     */
    @Deprecated
    public boolean stopNotify(
            BleDevice bleDevice, String uuid_service, String uuid_notify, boolean useCharacteristicDescriptor) {
        BleBluetooth bleBluetooth = multipleBluetoothController.getBleBluetooth(bleDevice);
        if (bleBluetooth == null) {
            return false;
        }
        boolean success =
                bleBluetooth
                        .newBleConnector()
                        .withUUIDString(uuid_service, uuid_notify)
                        .disableCharacteristicNotify(useCharacteristicDescriptor);
        if (success) {
            bleBluetooth.removeNotifyCallback(uuid_notify);
        }
        return success;
    }

    /**
     * stop indicate, remove callback
     *
     * @param bleDevice     bleDevice
     * @param uuid_service  uuid_service
     * @param uuid_indicate uuid_indicate
     * @return boolean
     */
    @Deprecated
    public boolean stopIndicate(BleDevice bleDevice, String uuid_service, String uuid_indicate) {
        return stopIndicate(bleDevice, uuid_service, uuid_indicate, false);
    }

    /**
     * stop indicate, remove callback
     *
     * @param bleDevice                   bleDevice
     * @param uuid_service                uuid_service
     * @param uuid_indicate               uuid_indicate
     * @param useCharacteristicDescriptor useCharacteristicDescriptor
     * @return boolean
     */
    @Deprecated
    public boolean stopIndicate(
            BleDevice bleDevice, String uuid_service, String uuid_indicate, boolean useCharacteristicDescriptor) {
        BleBluetooth bleBluetooth = multipleBluetoothController.getBleBluetooth(bleDevice);
        if (bleBluetooth == null) {
            return false;
        }
        boolean success =
                bleBluetooth
                        .newBleConnector()
                        .withUUIDString(uuid_service, uuid_indicate)
                        .disableCharacteristicIndicate(useCharacteristicDescriptor);
        if (success) {
            bleBluetooth.removeIndicateCallback(uuid_indicate);
        }
        return success;
    }

    /**
     * write
     *
     * @param bleDevice    bleDevice
     * @param uuid_service uuid_service
     * @param uuid_write   uuid_write
     * @param data         data
     * @param callback     callback
     */
    public void write(
            BleDevice bleDevice, String uuid_service, String uuid_write, byte[] data, BleWriteCallback callback) {
        write(bleDevice, uuid_service, uuid_write, data, true, callback);
    }

    /**
     * write
     *
     * @param bleDevice    bleDevice
     * @param uuid_service uuid_service
     * @param uuid_write   uuid_write
     * @param data         data
     * @param split        split
     * @param callback     callback
     */
    public void write(
            BleDevice bleDevice,
            String uuid_service,
            String uuid_write,
            byte[] data,
            boolean split,
            BleWriteCallback callback) {
        write(bleDevice, uuid_service, uuid_write, data, split, true, 0, callback);
    }

    /**
     * write
     *
     * @param bleDevice                 bleDevice
     * @param uuid_service              uuid_service
     * @param uuid_write                uuid_write
     * @param data                      data
     * @param split                     split
     * @param sendNextWhenLastSuccess   sendNextWhenLastSuccess
     * @param intervalBetweenTwoPackage intervalBetweenTwoPackage
     * @param callback                  callback
     */
    public void write(
            BleDevice bleDevice,
            String uuid_service,
            String uuid_write,
            byte[] data,
            boolean split,
            boolean sendNextWhenLastSuccess,
            long intervalBetweenTwoPackage,
            BleWriteCallback callback) {
        if (callback == null) {
            throw new IllegalArgumentException("BleWriteCallback can not be Null!");
        }

        if (data == null) {
            BleLog.error("data is Null!");
            callback.onWriteFailure(new OtherException("data is Null!"));
            return;
        }

        if (data.length > 20 && !split) {
            BleLog.worn("Be careful: data's length beyond 20! Ensure MTU higher than 23, or use spilt write!");
        }

        BleBluetooth bleBluetooth = multipleBluetoothController.getBleBluetooth(bleDevice);
        if (bleBluetooth == null) {
            callback.onWriteFailure(new OtherException("This device not connect!"));
        } else {
            if (split && data.length > getSplitWriteNum()) {
                new SplitWriter()
                        .splitWrite(
                                bleBluetooth,
                                uuid_service,
                                uuid_write,
                                data,
                                sendNextWhenLastSuccess,
                                intervalBetweenTwoPackage,
                                callback);
            } else {
                bleBluetooth
                        .newBleConnector()
                        .withUUIDString(uuid_service, uuid_write)
                        .writeCharacteristic(data, callback, uuid_write);
            }
        }
    }

    /**
     * read
     *
     * @param bleDevice    bleDevice
     * @param uuid_service uuid_service
     * @param uuid_read    uuid_read
     * @param callback     callback
     */
    public void read(BleDevice bleDevice, String uuid_service, String uuid_read, BleReadCallback callback) {
        if (callback == null) {
            throw new IllegalArgumentException("BleReadCallback can not be Null!");
        }

        BleBluetooth bleBluetooth = multipleBluetoothController.getBleBluetooth(bleDevice);
        if (bleBluetooth == null) {
            callback.onReadFailure(new OtherException("This device is not connected!"));
        } else {
            bleBluetooth
                    .newBleConnector()
                    .withUUIDString(uuid_service, uuid_read)
                    .readCharacteristic(callback, uuid_read);
        }
    }

    /**
     * read Rssi
     *
     * @param bleDevice bleDevice
     * @param callback  callback
     */
    public void readRssi(BleDevice bleDevice, BleRssiCallback callback) {
        if (callback == null) {
            throw new IllegalArgumentException("BleRssiCallback can not be Null!");
        }

        BleBluetooth bleBluetooth = multipleBluetoothController.getBleBluetooth(bleDevice);
        if (bleBluetooth == null) {
            callback.onRssiFailure(new OtherException("This device is not connected!"));
        } else {
            bleBluetooth.newBleConnector().readRemoteRssi(callback);
        }
    }

    /**
     * set Mtu
     *
     * @param bleDevice bleDevice
     * @param mtu       mtu
     * @param callback  callback
     */
    public void setMtu(BleDevice bleDevice, int mtu, BleMtuChangedCallback callback) {
        if (callback == null) {
            throw new IllegalArgumentException("BleMtuChangedCallback can not be Null!");
        }

        if (mtu > DEFAULT_MAX_MTU) {
            BleLog.error("requiredMtu should lower than 512 !");
            callback.onSetMTUFailure(new OtherException("requiredMtu should lower than 512 !"));
            return;
        }

        if (mtu < DEFAULT_MTU) {
            BleLog.error("requiredMtu should higher than 23 !");
            callback.onSetMTUFailure(new OtherException("requiredMtu should higher than 23 !"));
            return;
        }

        BleBluetooth bleBluetooth = multipleBluetoothController.getBleBluetooth(bleDevice);
        if (bleBluetooth == null) {
            callback.onSetMTUFailure(new OtherException("This device is not connected!"));
        } else {
            bleBluetooth.newBleConnector().setMtu(mtu, callback);
        }
    }

    /**
     * requestConnectionPriority
     *
     * @param connectionPriority Request a specific connection priority. Must be one of
     *                           {@link BlePeripheralDevice#CONNECTION_PRIORITY_NORMAL},
     *                           {@link BlePeripheralDevice#CONNECTION_PRIORITY_HIGH}
     *                           or {@link BlePeripheralDevice#CONNECTION_PRIORITY_LOW}.
     * @param bleDevice          bleDevice
     * @return boolean
     * @throws IllegalArgumentException If the parameters are outside of their
     *                                  specified range.
     */
    public boolean requestConnectionPriority(BleDevice bleDevice, int connectionPriority) {
        BleBluetooth bleBluetooth = multipleBluetoothController.getBleBluetooth(bleDevice);
        if (bleBluetooth == null) {
            return false;
        } else {
            return bleBluetooth.newBleConnector().requestConnectionPriority(connectionPriority);
        }
    }

    /**
     * is support ble?
     *
     * @return boolean
     */
    public boolean isSupportBle() {
        return true;
    }

    /**
     * Open bluetooth
     */
    public void openBluetooth() {
        if (bluetoothAdapter != null) {
            bluetoothAdapter.enableBt();
        }
    }

    /**
     * Disable bluetooth
     */
    public void closeBluetooth() {
        if (bluetoothAdapter != null) {
            bluetoothAdapter.disableBt();
        }
    }

    /**
     * judge Bluetooth is enable
     *
     * @return boolean
     */
    public boolean isBlueEnable() {
        if (bluetoothAdapter != null) {
            if (bluetoothAdapter.getBtState() == BluetoothHost.STATE_ON) {
                return true;
            } else {
                return false;
            }
        }
        return false;
    }

    /**
     * BleDevice
     *
     * @param bluetoothDevice bluetoothDevice
     * @return BleDevice
     */
    public BleDevice convertBleDevice(BlePeripheralDevice bluetoothDevice) {
        return new BleDevice(bluetoothDevice);
    }


    public BleDevice convertBleDevice(BleScanResult scanResult) {
        Map<Integer, byte[]> manu = scanResult.getManufacturerData();
        int key = 0;
        for (int keys : manu.keySet()) {
            key = keys;
        }
        BleLog.info("BleScanResult", key + "---3");
        byte[] manuByte = manu.get(key);
        String manufacturerStr = scanResult.getPeripheralDevice().getDeviceAddr();

        BleDevice device =
                new BleDevice(
                        scanResult.getPeripheralDevice(),
                        scanResult.getRssi(),
                        manuByte,
                        manufacturerStr,
                        scanResult.getTime(),
                        scanResult.isConnectable(),
                        scanResult.getAdvertiseFlag());
        return device;
    }


    /**
     * getBleBluetooth
     *
     * @param bleDevice bleDevice
     * @return BleBluetooth
     */
    public BleBluetooth getBleBluetooth(BleDevice bleDevice) {
        if (multipleBluetoothController != null) {
            return multipleBluetoothController.getBleBluetooth(bleDevice);
        }
        return null;
    }

    /**
     * getBluetoothGatt
     *
     * @param bleDevice bleDevice
     * @return BlePeripheralDevice
     */
    public BlePeripheralDevice getBluetoothGatt(BleDevice bleDevice) {
        BleBluetooth bleBluetooth = getBleBluetooth(bleDevice);
        if (bleBluetooth != null) {
            return bleBluetooth.getBluetoothGatt();
        }
        return null;
    }

    /**
     * getBluetoothGattServices
     *
     * @param bleDevice bleDevice
     * @return List
     */
    public List<GattService> getBluetoothGattServices(BleDevice bleDevice) {
        BlePeripheralDevice gatt = getBluetoothGatt(bleDevice);
        if (gatt != null) {
            return gatt.getServices();
        }
        return null;
    }

    /**
     * getBluetoothGattCharacteristics
     *
     * @param service service
     * @return List
     */
    public List<GattCharacteristic> getBluetoothGattCharacteristics(GattService service) {
        return service.getCharacteristics();
    }

    /**
     * removeConnectGattCallback
     *
     * @param bleDevice bleDevice
     */
    public void removeConnectGattCallback(BleDevice bleDevice) {
        BleBluetooth bleBluetooth = getBleBluetooth(bleDevice);
        if (bleBluetooth != null) {
            bleBluetooth.removeConnectGattCallback();
        }
    }

    /**
     * removeRssiCallback
     *
     * @param bleDevice bleDevice
     */
    public void removeRssiCallback(BleDevice bleDevice) {
        BleBluetooth bleBluetooth = getBleBluetooth(bleDevice);
        if (bleBluetooth != null) {
            bleBluetooth.removeRssiCallback();
        }
    }

    /**
     * removeMtuChangedCallback
     *
     * @param bleDevice bleDevice
     */
    public void removeMtuChangedCallback(BleDevice bleDevice) {
        BleBluetooth bleBluetooth = getBleBluetooth(bleDevice);
        if (bleBluetooth != null) {
            bleBluetooth.removeMtuChangedCallback();
        }
    }

    /**
     * removeNotifyCallback
     *
     * @param bleDevice   bleDevice
     * @param uuid_notify uuid_notify
     */
    public void removeNotifyCallback(BleDevice bleDevice, String uuid_notify) {
        BleBluetooth bleBluetooth = getBleBluetooth(bleDevice);
        if (bleBluetooth != null) {
            bleBluetooth.removeNotifyCallback(uuid_notify);
        }
    }

    /**
     * removeIndicateCallback
     *
     * @param bleDevice     bleDevice
     * @param uuid_indicate uuid_indicate
     */
    public void removeIndicateCallback(BleDevice bleDevice, String uuid_indicate) {
        BleBluetooth bleBluetooth = getBleBluetooth(bleDevice);
        if (bleBluetooth != null) {
            bleBluetooth.removeIndicateCallback(uuid_indicate);
        }
    }

    /**
     * removeWriteCallback
     *
     * @param bleDevice  bleDevice
     * @param uuid_write uuid_write
     */
    public void removeWriteCallback(BleDevice bleDevice, String uuid_write) {
        BleBluetooth bleBluetooth = getBleBluetooth(bleDevice);
        if (bleBluetooth != null) {
            bleBluetooth.removeWriteCallback(uuid_write);
        }
    }

    /**
     * removeReadCallback
     *
     * @param bleDevice bleDevice
     * @param uuid_read uuid_read
     */
    public void removeReadCallback(BleDevice bleDevice, String uuid_read) {
        BleBluetooth bleBluetooth = getBleBluetooth(bleDevice);
        if (bleBluetooth != null) {
            bleBluetooth.removeReadCallback(uuid_read);
        }
    }

    /**
     * javaDoc clearCharacterCallback
     *
     * @param bleDevice bleDevice
     */
    public void clearCharacterCallback(BleDevice bleDevice) {
        BleBluetooth bleBluetooth = getBleBluetooth(bleDevice);
        if (bleBluetooth != null) {
            bleBluetooth.clearCharacterCallback();
        }
    }

    /**
     * getScanSate
     *
     * @return BleScanState
     */
    public BleScanState getScanSate() {
        return BleScanner.getInstance().getScanState();
    }

    /**
     * getAllConnectedDevice
     *
     * @return List
     */
    public List<BleDevice> getAllConnectedDevice() {
        if (multipleBluetoothController == null) {
            return null;
        }
        return multipleBluetoothController.getDeviceList();
    }

    /**
     * @param bleDevice bleDevice
     * @return State of the profile connection. One of
     */
    public int getConnectState(BleDevice bleDevice) {
        if (bleDevice != null) {
            return bluetoothManager.getProfileConnState(ProfileBase.GATT);
        } else {
            return ProfileBase.STATE_DISCONNECTED;
        }
    }

    /**
     * isConnected
     *
     * @param bleDevice bleDevice
     * @return boolean
     */
    public boolean isConnected(BleDevice bleDevice) {
        return getConnectState(bleDevice) == ProfileBase.STATE_CONNECTED;
    }

    /**
     * isConnected
     *
     * @param mac mac
     * @return boolean
     */
    public boolean isConnected(String mac) {
        List<BleDevice> list = getAllConnectedDevice();
        for (BleDevice bleDevice : list) {
            if (bleDevice != null) {
                if (bleDevice.getMac().equals(mac)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * disconnect
     *
     * @param bleDevice bleDevice
     */
    public void disconnect(BleDevice bleDevice) {
        if (multipleBluetoothController != null) {
            multipleBluetoothController.disconnect(bleDevice);
        }
    }

    /**
     * disconnectAllDevice
     */
    public void disconnectAllDevice() {
        if (multipleBluetoothController != null) {
            multipleBluetoothController.disconnectAllDevice();
        }
    }

    /**
     * destroy
     */
    public void destroy() {
        if (multipleBluetoothController != null) {
            multipleBluetoothController.destroy();
        }
    }

    /**
     * 用于普通蓝牙扫描的时候，取消扫描观察者模式
     */
    public void unSubscribeBluetooth() {
        BleScanner.getInstance().unSubscribeBluetoothEvents();
    }

    private static final class ScanHandler extends EventHandler {
        ScanHandler(EventRunner looper) {
            super(looper);
        }

        @Override
        protected void processEvent(InnerEvent event) {
            super.processEvent(event);
        }
    }
}
